﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace Juzzbott.Ulib
{
	public class DataObjectManager
	{

		/// <summary>
		/// Define the integer node types.
		/// </summary>
		private static List<string> _intNodeTypes = new List<string>() { "1035", "1034", "1036" };

		/// <summary>
		/// Gets a list of all the data objects for the specified node under the specified property alias.
		/// </summary>
		/// <typeparam name="T">The IDataObject type of object to return.</typeparam>
		/// <param name="nodeID">The ID of the node to get the data objects from.</param>
		/// <param name="propertyAlias">The Alias of the property where the data objects are stored.</param>
		/// <returns>The list of data objects within the node.</returns>
		public static List<T> GetDataObjects<T>(int nodeID, string propertyAlias) where T : IDataObject
		{

			// Get the node 
			UmbracoNode parentNode = UmbracoNode.GetNode(nodeID);

			// Create the list of objects to return
			List<T> dataObjects = new List<T>();

			// If the parent node does not exist, return empty
			if (UmbracoNode.IsNull(parentNode))
			{
				return dataObjects;
			}

			// Get the values as raw xml ready for parsing
			string rawXml = parentNode.GetPropertyValue(propertyAlias);

			// If the valuea are empty, then return an empty list
			if (String.IsNullOrEmpty(rawXml))
			{
				return dataObjects;
			}

			// Create the XML document from the string
			XmlDocument doDoc = new XmlDocument();
			doDoc.LoadXml(rawXml);

			// Get all the item elements
			XmlNodeList itemNodes = doDoc.SelectNodes("/data/item");

			// Loop through each of the item elements
			foreach (XmlNode itemNode in itemNodes)
			{

				// Get the instance of the object
				T dataObject = mapDataObject<T>(itemNode);

				// If it's not null, add to the list
				if (dataObject != null)
				{
					dataObjects.Add(dataObject);
				}

			}

			// return the list of data objects
			return dataObjects;

		}

		/// <summary>
		/// Maps a data object from the XmlNode to an IDataObject object.
		/// </summary>
		/// <typeparam name="T">The type of the object to map.</typeparam>
		/// <param name="theNode">The XMLNode containing data to map.</param>
		/// <returns>The mapped data object.</returns>
		private static T mapDataObject<T>(XmlNode theNode) where T : IDataObject
		{

			try
			{

				// create an instance of the object
				T objInstance = Activator.CreateInstance<T>();

				// Get the type of the object
				Type objType = typeof(T);

				// If the node has an id attribute, set the ID of the object
				if (theNode.Attributes["id"] != null && !String.IsNullOrEmpty(theNode.Attributes["id"].Value))
				{
					// Get the ID as an int.
					int id = 0;
					Int32.TryParse(theNode.Attributes["id"].Value, out id);

					// Get the ID property of the object, which has to exist due to the where T constraint of IDataType
					PropertyInfo idProp = objType.GetProperty("ID");

					// Set the value of the property
					if (idProp != null)
					{
						idProp.SetValue(objInstance, id, null);
					}

				}

				// Loop through all the child nodes 
				foreach (XmlNode childNode in theNode.ChildNodes)
				{

					// Get the property name
					string propertyName = getPropertyName(childNode.Name);

					// Get the property by the name
					PropertyInfo prop = objType.GetProperty(propertyName);

					// If the prop is not null, set the value
					if (prop != null)
					{
						// Set the property value
						prop.SetValue(objInstance, Convert.ChangeType(childNode.InnerText, prop.PropertyType), null);
					}

				}

				// return the objInstance
				return objInstance;

			}
			catch (Exception ex)
			{
				// TODO: some form of logging
				return default(T);
			}

		}

		/// <summary>
		/// Gets a property name from a node name.
		/// </summary>
		/// <param name="name">The name of the node to get as a property name.</param>
		/// <returns>The property name.</returns>
		private static string getPropertyName(string name)
		{
			// return the formatted property name
			return String.Format("{0}{1}",
				name.Substring(0, 1).ToUpper(),
				name.Substring(1));
		}

	}
}
